package com.wind;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class WindDatastore {

	private static Map<String, WindType> types = initTypes();
	
	private static  Map<String,WindType> initTypes() {
		Map<String,WindType> types = new HashMap<String,WindType>();
		return types;
	}
	
	public static WindType addType(String code, String name, WindDatastoreService service) {
		WindType type = new WindType(code, name, service);
		types.put(type.getCode(), type);
		return type;
	}
	
	public static JSON execute(JSON request) {
		JSON response = new JSON();
		String operation = request.getString("operation");
		if(operation.equals("datastore.delete")) {
			 executeDelete(request, response);
		} else if (operation.equals("datastore.insert")) {
			 executeInsert(request, response);
		} else if (operation.equals("datastore.update")) {
			 executeUpdate(request, response);
		} else if (operation.equals("datastore.list")) {
			 executeList(request, response);
		} else {
			throw new RuntimeException("Ivalid (or not present) operation in request: " + request);
		}
		return response;
	}

	private static WindDatastoreService getService(JSON request) {
		String typeCode = request.getString("type");
		if(typeCode==null || typeCode.length()<=0) {
			throw new RuntimeException("Invalid (or not present) type not found in request: " + request);
		}
		
		WindType typeImplementation = types.get(typeCode);
		if(typeImplementation==null) {
			throw new RuntimeException("Not registered type: " + typeCode);
		}
		
		WindDatastoreService service = typeImplementation.getService();
		
		return service;
	}
	
	private static void executeDelete(JSON jsonRequest, JSON jsonResponse) {
		JSON jsonErrors = jsonResponse.addArray("errors");
		WindDatastoreService service = getService(jsonRequest);
		service.executeDelete(jsonRequest, jsonResponse, jsonErrors);
	}
	
	private static void executeUpdate(JSON jsonRequest, JSON jsonResponse) {
		JSON jsonErrors = jsonResponse.addArray("errors");
		JSON jsonEntity = jsonRequest.get("entity");
		WindEntity entity = jsonToEntity(jsonEntity);
		List<WindConstraintViolation> violations = WindConstraints.checkForUpdate(entity);
		if(violations.size()>0) {
			addConstraintViolationsToJsonErrors(violations, jsonErrors);
		}
		
		WindDatastoreService service = getService(jsonRequest);
		service.executeUpdate(jsonRequest, jsonResponse, jsonErrors, entity);
	}

	private static void executeInsert(JSON jsonRequest, JSON jsonResponse) {
		JSON jsonErrors = jsonResponse.addArray("errors");
		JSON jsonEntity = jsonRequest.get("entity");
		WindEntity entity = jsonToEntity(jsonEntity);
		List<WindConstraintViolation> violations = WindConstraints.checkForInsert(entity);
		if(violations.size()>0) {
			addConstraintViolationsToJsonErrors(violations, jsonErrors);
		}
		
		WindDatastoreService service = getService(jsonRequest);
		service.executeInsert(jsonRequest, jsonResponse, jsonErrors, entity);
	}

	private static void executeList(JSON jsonRequest, JSON jsonResponse) {
		JSON jsonErrors = jsonResponse.addArray("errors");
		WindDatastoreService service = getService(jsonRequest);
		service.executeList(jsonRequest, jsonResponse, jsonErrors);
	}
	
	private static WindEntity jsonToEntity(JSON jsonEntity) {
		WindEntity entity = null;

		String typeCode = jsonEntity.getString("type");
		if(typeCode==null || typeCode.length()<=0) {
			throw new RuntimeException("Invalid (or not present) type not found in json entity: " + jsonEntity);
		}
		
		WindType typeImplementation = types.get(typeCode);
		if(typeImplementation==null) {
			throw new RuntimeException("Not registered type: " + typeCode);
		}
		
		entity = new WindEntity(typeImplementation);
		
		List<JSON> children = jsonEntity.getChildren();
		Iterator<JSON> it = children.iterator();
		while(it.hasNext()) {
			JSON child = it.next();
			if(child.isString()) {
				if(!child.getName().equals("type")) {
					String value = child.getStringValue();
					entity.setAttribute(child.getName(), value);
				}
			}
		}
		
		return entity;
	}
	
	private static void addConstraintViolationsToJsonErrors(List<WindConstraintViolation> violations, JSON errors) {
		Iterator<WindConstraintViolation> it = violations.iterator();
		while(it.hasNext()) {
			WindConstraintViolation violation = it.next();
			JSON jsonError = errors.addArrayElement();
			jsonError.addAttribute("attribute", violation.getConstraint().getAttribute());
			
			String text = getTextFromConstraintViolation(violation);
			jsonError.addAttribute("text", text);

			JSON jsonErrorParameters = jsonError.addArray("parameters");

			if(violation.getParameters()!=null) {
				Iterator<String> itParameters = violation.getParameters().iterator();
				while(itParameters.hasNext()) {
					String parameter = itParameters.next();
					JSON jsonParameter = jsonErrorParameters.addArrayElement();
					jsonParameter.setStringValue(parameter.toString());
				}
			}
		}
	}
	
	private static String getTextFromConstraintViolation(WindConstraintViolation violation) {
		String text = "Constraint violation";
		
		WindConstraint constraint = violation.getConstraint();
		if (constraint instanceof WindConstraintNotNull) {
			text = "Attribute cannot be null";
		} else if (constraint instanceof WindConstraintUnique) {
			text = "Attribute must be null";
		}
		
		return text;
	}

}
